package org.fixies.spreadsheet.interpreter;

import org.fixies.spreadsheet.Unit;

import java.util.*;

/**
 * 子表を表すノード
 *
 * @author Kawasaki
 */
public class UnitNode {

    private static final String ALLOW_EMPTY_METADATA = "START";

    private static final String END_OF_DATASET_EXP = "#END";

    /**
     * メタデータノード
     */
    private MetadataNode metadataNode_ = new MetadataNode();

    private List<List<String>> dataRows_;

    private String origin_;

    /**
     * メタデータ
     */
    private Map<String, String> metadata_ = new HashMap<String, String>(0);

    public UnitNode(String origin) {
        origin_ = origin;
    }

    /**
     * 評価する。
     *
     * @param context コンテクスト
     */
    public void evaluate(SheetContext context) {

        metadataNode_.evaluate(context);

        dataRows_ = new ArrayList<List<String>>();
        String[] line = context.current();
        while (!isEndOfData(line)) {
            dataRows_.add(Arrays.asList(line));
            line = context.next();
        }
    }

    /**
     * 解析結果を取得する。
     *
     * @return 解析結果
     */
    public Unit getResult() {
        // 子表自身のメタデータ
        Map<String, String> childMetadata = metadataNode_.getResult();
        // 親シートのメタデータとマージする。
        metadata_.putAll(childMetadata);
        return createDataSet(origin_, metadata_, dataRows_);
    }

    /**
     * 子表を生成する。<br/>
     * DataSetインタフェースの実装クラスを変更する場合は、本メソッドをオーバーライドする。
     *
     * @param origin   起源
     * @param metadata メタデータ
     * @param dataRows データ行
     * @return 子表インスタンス
     */
    protected Unit createDataSet(String origin, Map<String, String> metadata,
                                 List<List<String>> dataRows) {
        return new Unit(origin, metadata, dataRows);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("METADATA=[").append(metadataNode_.toString()).append("] ");
        sb.append("DATA=[").append(dataRows_.toString()).append("]");
        return sb.toString();
    }

    /**
     * データの終端であるか判定する。
     *
     * @param line 行データ
     * @return 判定結果
     */
    private boolean isEndOfData(String[] line) {
        if (line == null) return true;
        // 空行か
        if (line.length == 0) {
            // 空行を含むことが許容されてない場合、終端とする。
            return !allowEmptyRow();
        }
        // 終端文字列なら終了
        return END_OF_DATASET_EXP.equals(line[0]);
    }

    /**
     * 空行を許容するか。<br/>
     * #STARTで始まっていれば空行を許容する。
     *
     * @return 判定結果
     */
    private boolean allowEmptyRow() {
        return metadataNode_.getResult().containsKey(ALLOW_EMPTY_METADATA);
    }

}
